﻿using System;
using System.Linq;
using Magellan.Utilities;

namespace Magellan.Routing
{
    public class Route : IRoute
    {
        private readonly string _name;
        private readonly string _pathSpecification;
        private readonly Func<IRouteHandler> _routeHandler;
        private readonly IRouteValidator _validator;
        private readonly IRouteParser _parser;
        private readonly RouteValueDictionary _defaults;
        private readonly RouteValueDictionary _constraints;
        private ParsedRoute _parsedRoute;

        public Route(string name, string pathSpecification, Func<IRouteHandler> routeHandler, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null, IRouteParser parser = null, IRouteValidator validator = null)
        {
            Guard.ArgumentNotNullOrEmpty(name, "name");
            Guard.ArgumentNotNull(routeHandler, "routeHandler");

            pathSpecification = pathSpecification ?? string.Empty;
            _name = name;
            _pathSpecification = pathSpecification;
            _routeHandler = routeHandler;
            _validator = validator ?? new DefaultRouteValidator();
            _defaults = defaults ?? new RouteValueDictionary();
            _constraints = constraints ?? new RouteValueDictionary();
            _parser = parser ?? new RouteParser(
                new ParameterSegmentRecognizer(),
                new LiteralSegmentRecognizer(),
                new CatchAllParameterSegmentRecognizer()
                );
        }

        public string Name
        {
            get { return _name; }
        }

        public RouteValueDictionary Defaults
        {
            get { return _defaults; }
        }

        public RouteValueDictionary Constraints
        {
            get { return _constraints; }
        }

        public string Specification
        {
            get { return _pathSpecification; }
        }

        public IRouteValidator Validator
        {
            get { return _validator; }
        }

        public IRouteParser Parser
        {
            get { return _parser; }
        }

        public void Prepare()
        {
            if (_parsedRoute == null)
            {
                _parsedRoute = _parser.Parse(this, _pathSpecification, _defaults, _constraints);
                var result = _validator.Validate(_parsedRoute);
                if (!result.Success)
                {
                    throw new InvalidRouteException(
                        this,
                        result,
                        string.Format("Invalid route: {0} {1}", Name, string.Join("", result.Errors.Select(x => Environment.NewLine + " - " + x).ToArray()))
                        );
                }
            }
        }

        public IRouteHandler CreateRouteHandler()
        {
            return _routeHandler();
        }

        public RouteMatch MatchPathToRoute(string request)
        {
            Prepare();
            return _parsedRoute.MatchPathToRoute(this, request);
        }

        public PathMatch MatchRouteToPath(RouteValueDictionary values)
        {
            Prepare();
            return _parsedRoute.MatchRouteToPath(this, values);
        }
    }
}